//
//  MGOssManager.m
//  MoneyGoal_iOS
//
//  Created by yyb on 2022/8/23.
//  Copyright © 2022 Moneyease CO,. LTD. All rights reserved.
//

#import "MGOssManager.h"

static char * const MGOSSManagerQueue = "MGOSSManager.concurrent.queue";

#define Bucket_Name @"moneyease-moneygoal"
#define Bucket_Name_Avatar @"moneyease-icons"

@interface MGOssManager()
@property (nonatomic, strong) OSSClient *client;

@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@property (nonatomic, strong) dispatch_queue_t queue;

@end

@implementation MGOssManager

+ (MGOssManager *)shared {
    static MGOssManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[MGOssManager alloc] init];
    });
    return manager;
}

- (instancetype)init {
    self = [super init];
    if (!self) return nil;
    
    _semaphore = dispatch_semaphore_create(0);
    _queue = dispatch_queue_create(MGOSSManagerQueue, DISPATCH_QUEUE_CONCURRENT);

    NSString *authUrl = [MGReqManager.shared requestURL:@"/oss/auth"];
    NSString *auth = [NSString stringWithFormat:@"%@?device_info=%@",authUrl,[[UIDevice currentDevice].identifierForVendor.UUIDString md5]];
    id<OSSCredentialProvider> credential = [[OSSAuthCredentialProvider alloc] initWithAuthServerUrl:auth];

    _client = [[OSSClient alloc] initWithEndpoint:@"oss-cn-hangzhou.aliyuncs.com" credentialProvider:credential];
    
    return self;
}

+ (BOOL)shouldUpload
{
    return MGConfigManager.shared.dataModel.enableOSS && [MGUserModel isUserLogin];
}

- (void)uploadData:(NSData *)data key:(NSString *)key bucket:(NSString *)bucket
     progressBlock:(nullable void (^)(int64_t, int64_t))progressBlock
     completeBlock:(nullable YYBEmptyCompleteBlock)completeBlock errorBlock:(nullable YYBErrorBlock)errorBlock
{
    OSSPutObjectRequest *put = [OSSPutObjectRequest new];
    put.bucketName = bucket;
    put.objectKey = key;
    put.uploadingData = data;
    put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
        if (progressBlock) {
            progressBlock(totalByteSent,totalBytesExpectedToSend);
        }
    };
    
    OSSTask *ossTask = [_client putObject:put];
    [ossTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error) {
            if (completeBlock) {
                completeBlock();
            }
        } else {
            if (errorBlock) {
                errorBlock(task.error);
            }
        }
        return nil;
    }];
}

- (void)getDataWithKey:(NSString *)key bucket:(NSString *)bucket
     completeBlock:(void (^)(NSDictionary * _Nonnull))completeBlock errorBlock:(nullable void (^)(NSError * _Nonnull))errorBlock
{
    OSSGetObjectRequest *request = [OSSGetObjectRequest new];
    request.bucketName = bucket;
    request.objectKey = key;
    
    OSSTask *ossTask = [_client getObject:request];
    [ossTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error) {
            OSSGetObjectResult *result = task.result;
            id value = [NSJSONSerialization JSONObjectWithData:result.downloadedData options:kNilOptions error:nil];
            if (completeBlock) {
                completeBlock(value);
            }
        } else {
            if (errorBlock) {
                errorBlock(task.error);
            }
        }
        return nil;
    }];
}

- (void)deleteObjectKey:(NSString *)objectKey completeBlock:(nullable YYBEmptyCompleteBlock)completeBlock errorBlock:(nullable YYBErrorBlock)errorBlock
{
    OSSDeleteObjectRequest * delete = [OSSDeleteObjectRequest new];
    delete.bucketName = Bucket_Name;
    delete.objectKey = objectKey;

    OSSTask * deleteTask = [_client deleteObject:delete];
    [deleteTask continueWithBlock:^id(OSSTask *task) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (!task.error) {
                // 删除成功
                if (completeBlock) {
                    completeBlock();
                }
            } else {
                if (errorBlock) {
                    errorBlock(task.error);
                }
            }
        });
        return nil;
    }];
}

// 获取所有子数据
- (void)getContentsWithPrefix:(NSString *)prefix bucket:(NSString *)bucket completeBlock:(nullable void (^)(NSArray * _Nullable))completeBlock errorBlock:(nullable void (^)(NSError * _Nullable))errorBlock
{
    OSSGetBucketRequest *bucketReq = [OSSGetBucketRequest new];
    bucketReq.bucketName = bucket;
    bucketReq.prefix = prefix;
    
    OSSTask *ossTask = [_client getBucket:bucketReq];
    [ossTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error) {
            OSSGetBucketResult *result = task.result;
            if (completeBlock) {
                if (result.contents != nil) {
                    completeBlock(result.contents);
                } else {
                    completeBlock(nil);
                }
            }
        } else {
            if (errorBlock) {
                errorBlock(task.error);
            }
        }
        return nil;
    }];
}

- (void)getKeysWithPrefix:(NSString *)prefix bucket:(NSString *)bucket completeBlock:(nullable void (^)(NSArray * _Nullable))completeBlock errorBlock:(nullable void (^)(NSError * _Nullable))errorBlock
{
    [self getContentsWithPrefix:prefix bucket:bucket completeBlock:^(NSArray * _Nullable contents) {
        NSArray *keys = [contents map:^id(NSDictionary *obj, NSInteger index) {
            return obj[@"Key"];
        }];
        
        if (completeBlock) {
            completeBlock(keys);
        }
    } errorBlock:errorBlock];
}

- (void)saveToOssWithDataModels:(NSArray *)dataModels completeBlock:(YYBEmptyCompleteBlock)completeBlock errorBlock:(YYBErrorBlock)errorBlock
{
    dispatch_async(self.queue, ^{
        for (NSInteger index = 0; index < dataModels.count; index ++) {
            MGAccessorModel *dataModel = dataModels[index];
            // 默认的不允许被上传
            BOOL shouldUpload = dataModel.shouldUpload;
            if (shouldUpload && !dataModel.isStatic) {
                BOOL isLogin = [MGUserModel isUserLogin];
                if (!dataModel.create_userId.isExist && isLogin) {
                    dataModel.create_userId = MGUserModel.user.uid;
                }
                NSData *data = [dataModel yy_modelToJSONData];
                @weakify(self);
                [self uploadData:data key:dataModel.getOssKey bucket:Bucket_Name progressBlock:nil completeBlock:^{
                    @strongify(self);
                    dispatch_semaphore_signal(self.semaphore);
                } errorBlock:^(NSError * _Nonnull error) {
                    @strongify(self);
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (errorBlock) {
                            errorBlock(error);
                        }
                    });
                    dispatch_semaphore_signal(self.semaphore);
                }];
                dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completeBlock) {
                completeBlock();
            }
        });
    });
}

- (void)uploadAvatar:(UIImage *)avatar completeBlock:(nullable void (^)(NSString * _Nonnull))completeBlock errorBlock:(nullable void (^)(NSError * _Nullable))errorBlock
{
    NSData *data = [UIImage compressImage:avatar toMaxFileSize:2048];
    NSString *uniqueId = [NSString stringWithFormat:@"%ld.png", [NSString createUniqueId]];
    NSString *key = [NSString stringWithFormat:@"moneygoal/avatars/%@/%@", [MGUserModel user].uid, uniqueId];
    [self uploadData:data key:key bucket:Bucket_Name_Avatar progressBlock:nil completeBlock:^{
        if (completeBlock) {
            completeBlock(key);
        }
    } errorBlock:errorBlock];
}

- (void)getDataModelsWithPrefix:(NSString *)prefix completeBlock:(void (^)(NSArray * _Nonnull objs))completeBlock errorBlock:(YYBErrorBlock)errorBlock
{
    @weakify(self);
    [self getKeysWithPrefix:prefix bucket:Bucket_Name completeBlock:^(NSArray * _Nonnull keys) {
        @strongify(self);
        if (keys != nil && keys.count > 0) {
            dispatch_async(self.queue, ^{
                __block NSMutableArray *contents = [NSMutableArray new];
                for (NSInteger index = 0; index < keys.count; index ++) {
                    NSString *key = [keys objectAtIndex:index];
                    [self getDataWithKey:key bucket:Bucket_Name completeBlock:^(id data) {
                        dispatch_semaphore_signal(self.semaphore);
                        [contents addObject:data];
                        if (index == keys.count - 1) { // 是否是最后一个
                            dispatch_async_on_main_queue(^{
                                if (completeBlock) {
                                    completeBlock(contents);
                                }
                            });
                        }
                    } errorBlock:^(NSError * _Nonnull error) {
                        dispatch_semaphore_signal(self.semaphore);
                        dispatch_async_on_main_queue(^{
                            if (errorBlock) {
                                errorBlock(error);
                            }
                        });
                    }];
                    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
                }
            });
        } else {
            dispatch_async_on_main_queue(^{
                if (completeBlock) {
                    completeBlock(@[]);
                }
            });
        }
    } errorBlock:^(NSError * _Nonnull error) {
        dispatch_async_on_main_queue(^{
            if (errorBlock) {
                errorBlock(error);
            }
        });
    }];
}

- (void)getDataModelsWithPrefix:(NSString *)prefix dataClass:(Class)dataClass completeBlock:(void (^)(NSArray * _Nonnull))completeBlock errorBlock:(YYBErrorBlock)errorBlock
{
    [self getDataModelsWithPrefix:prefix completeBlock:^(NSArray * _Nonnull objs) {
        NSArray *recordObjs = [NSArray yy_modelArrayWithClass:dataClass json:objs];
        if (completeBlock) {
            completeBlock(recordObjs);
        }
    } errorBlock:errorBlock];
}

@end
